#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2021 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
import argparse
import logging
from ftrace_format_parser import FtraceEventCodeGenerator

FTRACE_BUNDLE_PROTO = 'ftrace_event.proto'
AUTO_GENERATED_GNI = 'autogenerated.gni'

THIS_FILE = os.path.basename(__file__)
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
    level=logging.INFO)
logger = logging.getLogger(THIS_FILE)

PROTO_COMMON_HEADER = '''\
// THIS FILE IS GENERATED BY {}, PLEASE DON'T EDIT IT!
// Copyright (c) 2021 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

syntax = "proto3";

option optimize_for = LITE_RUNTIME;

'''.format(THIS_FILE)

GN_COPYRIGHT_HEADER = '''\
# THIS FILE IS GENERATE BY {}, PLEASE DON'T EDIT IT!
# Copyright (C) 2021 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
'''.format(THIS_FILE)

MAX_EVENTS_IN_CATEGORY = 100

PROTO_MESSAGE_HEAD = '''\
message FtraceEvent {
    uint64 timestamp = 1;
    int32 tgid = 2;
    string comm = 3;

    message CommonFileds {
        uint32 type = 1;
        uint32 flags = 2;
        uint32 preempt_count = 3;
        int32 pid = 4;
    };
    CommonFileds common_fields = 50;

    oneof event {
'''


def to_camel_case(name):
    return ''.join([p.capitalize() for p in name.split('_')])


def fix_field_name(name):
    replace_map = {
        'errno': 'error_code',
        'sa_handler': 'sig_handler',
        'sa_flags': 'sig_flags',
        'new': 'seq_new'
    }
    if name in replace_map:
        name = replace_map[name]
    return str.lower(name)


class FtraceEventProtoGenerator(FtraceEventCodeGenerator):
    def __init__(self, events_dir, allow_list):
        super().__init__(events_dir, allow_list)

    def generate_code(self):
        proto_file_list = []
        for category in self.grouped_event_formats:
            proto_name = '{}.proto'.format(category)
            proto_path = os.path.join(self.output_dir, proto_name)
            proto_file_list.append(proto_path)
            self.generate_event_proto(category, proto_path)

        bundle_proto_head = [PROTO_COMMON_HEADER]
        bundle_proto_body = [PROTO_MESSAGE_HEAD]
        event_category_count = 0
        for category in self.grouped_event_formats:
            event_category_count += 1
            proto_name = '{}.proto'.format(category)
            bundle_proto_head.append('import "{}";\n'.format(proto_name))
            for i in range(len(self.grouped_event_formats[category])):
                event_format = self.grouped_event_formats[category][i]
                message_name = '{}_format'.format(event_format.name)
                message_type = to_camel_case(message_name)
                message_id = event_category_count * 100 + i
                message_name = fix_field_name(message_name)
                bundle_proto_body.append(
                    '        {} {} = {};\n'.format(message_type,
                                                   message_name,
                                                   message_id))
        bundle_proto_head.append('\n')
        bundle_proto_body.append('    }\n')
        bundle_proto_body.append('}\n')

        bundle_proto_path = os.path.join(self.output_dir, FTRACE_BUNDLE_PROTO)
        proto_file_list.append(bundle_proto_path)
        logger.info('Generate {} ...'.format(bundle_proto_path))
        with open(bundle_proto_path, 'w+') as f:
            f.writelines(bundle_proto_head + bundle_proto_body)

        protos_gni_path = os.path.join(self.output_dir, AUTO_GENERATED_GNI)
        logger.info('Generate {} ...'.format(protos_gni_path))
        with open(protos_gni_path, 'w+') as f:
            f.write(GN_COPYRIGHT_HEADER)
            # proto sources
            f.write('auto_generated_ftrace_proto_sources = [\n')
            for proto_path in proto_file_list:
                f.write('  "{}",\n'.format(os.path.basename(proto_path)))
            f.write(']\n')

    def generate_event_proto(self, category, proto_path):
        with open(proto_path, 'w+') as f:
            f.write(PROTO_COMMON_HEADER)
            f.write('// category: {}\n'.format(category))
            logger.info('Generate {} ...'.format(proto_path))
            for event_format in self.grouped_event_formats[category]:
                logger.debug(
                    '{}/{}:'.format(event_format.category, event_format.name))
                message_name = '{}_format'.format(event_format.name)
                message_type = to_camel_case(message_name)
                device_format_file = '{}/{}/{}/format'.format(
                    '/sys/kernel/debug/tracing/events',
                    event_format.category, event_format.name)

                field_count = 1
                f.write('// {}\n'.format(device_format_file))
                f.write('message {} {{\n'.format(message_type))
                for field in event_format.remain_fields:
                    type_string = field.to_proto_type().to_string()
                    field_name = fix_field_name(field.name)
                    logger.debug('    {}: {}'.format(field, type_string))
                    f.write('    {} {} = {};\n'.format(type_string, field_name,
                                                       field_count))
                    field_count += 1
                f.write('}\n')
                f.write('\n')


def main():
    parser = argparse.ArgumentParser(
        description='FTrace proto code generator.')
    parser.add_argument('-a', dest='allow_list', required=True, type=str,
                        help='event allow list file path')
    parser.add_argument('-e', dest='events_dir', required=True, type=str,
                        help='event formats directory')
    parser.add_argument('-o', dest='output_dir', required=True, type=str,
                        help='code file output directory')

    args = parser.parse_args(sys.argv[1:])
    events_dir = args.events_dir
    output_dir = args.output_dir
    allow_list = args.allow_list

    generator = FtraceEventProtoGenerator(events_dir, allow_list)
    generator.generate(output_dir)


if __name__ == '__main__':
    main()
